//
//  ZNWBRefreshControl.swift
//  ZNWeibo
//
//  Created by FunctionMaker on 2017/8/25.
//  Copyright © 2017年 FunctionMaker. All rights reserved.
//

import UIKit

/// 下拉刷新控件的状态
///
/// - Normal: 普通状态，不进行任何操作
/// - Pulling: 拖拽程度超过临界点，结束拖拽将进行刷新
/// - willRefresh: 拖拽程度超过临界点，刷新控件将要进行刷新
enum ZNWBRefreshState {
    case Normal
    case Pulling
    case willRefresh
}

/// 处理下拉刷新的逻辑
class ZNWBRefreshControl: UIControl {
    
    // 弱引用父视图，防止引用循环
    private weak var scrollView: UIScrollView?
    
    fileprivate lazy var refreshView = ZNWBRefreshView.refreshView()
    
    init() {
        super.init(frame: CGRect())
        
        setupUI()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        setupUI()
    }
    
    
    func beginRefreshing() {
        print("开始刷新")
        
        guard let scrollView = scrollView
        
        else {
            return
        }
        
        // 如果当前已经在刷新，就不在开始刷新，防止重复调整表格内容间距
        if refreshView.state == .willRefresh {
            return
        }
        
        refreshView.state = .willRefresh
        
        var contentInset = scrollView.contentInset
        contentInset.top += ZNWBRefreshOffset
        
        scrollView.contentInset = contentInset;
        refreshView.parentViewHeight = ZNWBRefreshOffset
        
        // 在进入表格视图时，会重复调用beginAnimation，导致contentInset缩进错误
        //sendActions(for: .valueChanged)
    }
    
    func endRefreshing() {
        print("结束刷新")
        
        guard let scrollView = scrollView
        
        else {
            return
        }
        
        // 如果当前没有刷新，就不需要结束，防止重复调整表格内容间距
        if refreshView.state != .willRefresh {
            return
        }
        
        refreshView.state = .Normal
        
        var contentInset = scrollView.contentInset
        contentInset.top -= ZNWBRefreshOffset
        
        scrollView.contentInset = contentInset
    }
    
    // 此时，子视图还没有完成位置的设置，自动约束是以父视图为参考来确定视图的frame
    override func willMove(toSuperview newSuperview: UIView?) {
        super.willMove(toSuperview: newSuperview)
        
        guard let newSuperview = newSuperview as? UIScrollView
        else {
            return
        }
        
        scrollView = newSuperview
        
        // 能够添加KVO，说明scrolView一定有值
        scrollView!.addObserver(self, forKeyPath: "contentOffset", options: [], context: nil)
    }
    
    // 监听俯视图的内容偏移量
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        guard let scrollView = scrollView
            
        else {
            return
        }
        
        let height = -(scrollView.contentInset.top + scrollView.contentOffset.y)
        
        // 如果是在原点时向上拖拽，什么都不做
        if height < 0 {
            return
        }
        
        // 更新刷新控件的frame
        // scrollView的子视图会跟着contentOffset的位置变化而变化，即原点默认为内容视图的原点
        self.frame = CGRect(x: 0,
                            y: -height,
                            width: scrollView.bounds.width,
                            height: height)
        
        // 如果正在刷新中，下拉刷新控件不随父视图高度改变而改变
        // **把代码放在最合适的地方，从因果关系上去追溯**
        if refreshView.state != .willRefresh {
            refreshView.parentViewHeight = height
        }
        
        if scrollView.isDragging {
            if height > ZNWBRefreshOffset && refreshView.state == .Normal {
                refreshView.state = .Pulling
            } else if height <= ZNWBRefreshOffset && refreshView.state == .Pulling {
                refreshView.state = .Normal
            }
        } else {
            if refreshView.state == .Pulling {
                // 通过改变contentInset显示刷新控件
                beginRefreshing()
                
                // 发送事件，进行目标值改变的回调
                sendActions(for: .valueChanged)
            }
        }
    }
    
    // 第三方框架实现下拉刷新：在WillMoveToSuperView添加KVO监听scrollView的contentOffset
    // 在removeFromSuperview时，移除对scrollView的监听
    override func removeFromSuperview() {
        // 移除KVO监听者，不移除程序会崩溃
        superview?.removeObserver(self, forKeyPath: "contentOffset")
        
        super.removeFromSuperview()
    }
}

extension ZNWBRefreshControl {
    fileprivate func setupUI() {
        backgroundColor = superview?.backgroundColor
        
        // 超出父视图部分不显示，最初调试时使用过
        //clipsToBounds = true
        
        addSubview(refreshView)
        
        // 去掉autoresing，使自动布局生效
        refreshView.translatesAutoresizingMaskIntoConstraints = false
        
        addConstraint(NSLayoutConstraint(item: refreshView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1.0, constant: 0.0))
        addConstraint(NSLayoutConstraint(item: refreshView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: 0.0))
        
        addConstraint(NSLayoutConstraint(item: refreshView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: refreshView.bounds.width))
        addConstraint(NSLayoutConstraint(item: refreshView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: refreshView.bounds.height))
    }
}
